home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / DB / mssql.php < prev    next >
Encoding:
PHP Script  |  2005-07-07  |  26.9 KB  |  915 lines

  1. <?php
  2.  
  3. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  4.  
  5. /**
  6.  * The PEAR DB driver for PHP's mssql extension
  7.  * for interacting with Microsoft SQL Server databases
  8.  *
  9.  * PHP versions 4 and 5
  10.  *
  11.  * LICENSE: This source file is subject to version 3.0 of the PHP license
  12.  * that is available through the world-wide-web at the following URI:
  13.  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  14.  * the PHP License and are unable to obtain it through the web, please
  15.  * send a note to license@php.net so we can mail you a copy immediately.
  16.  *
  17.  * @category   Database
  18.  * @package    DB
  19.  * @author     Sterling Hughes <sterling@php.net>
  20.  * @author     Daniel Convissor <danielc@php.net>
  21.  * @copyright  1997-2005 The PHP Group
  22.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  23.  * @version    CVS: $Id: mssql.php,v 1.83 2005/03/07 18:24:51 danielc Exp $
  24.  * @link       http://pear.php.net/package/DB
  25.  */
  26.  
  27. /**
  28.  * Obtain the DB_common class so it can be extended from
  29.  */
  30. require_once 'DB/common.php';
  31.  
  32. /**
  33.  * The methods PEAR DB uses to interact with PHP's mssql extension
  34.  * for interacting with Microsoft SQL Server databases
  35.  *
  36.  * These methods overload the ones declared in DB_common.
  37.  *
  38.  * @category   Database
  39.  * @package    DB
  40.  * @author     Sterling Hughes <sterling@php.net>
  41.  * @author     Daniel Convissor <danielc@php.net>
  42.  * @copyright  1997-2005 The PHP Group
  43.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  44.  * @version    Release: 1.7.6
  45.  * @link       http://pear.php.net/package/DB
  46.  */
  47. class DB_mssql extends DB_common
  48. {
  49.     // {{{ properties
  50.  
  51.     /**
  52.      * The DB driver type (mysql, oci8, odbc, etc.)
  53.      * @var string
  54.      */
  55.     var $phptype = 'mssql';
  56.  
  57.     /**
  58.      * The database syntax variant to be used (db2, access, etc.), if any
  59.      * @var string
  60.      */
  61.     var $dbsyntax = 'mssql';
  62.  
  63.     /**
  64.      * The capabilities of this DB implementation
  65.      *
  66.      * The 'new_link' element contains the PHP version that first provided
  67.      * new_link support for this DBMS.  Contains false if it's unsupported.
  68.      *
  69.      * Meaning of the 'limit' element:
  70.      *   + 'emulate' = emulate with fetch row by number
  71.      *   + 'alter'   = alter the query
  72.      *   + false     = skip rows
  73.      *
  74.      * @var array
  75.      */
  76.     var $features = array(
  77.         'limit'         => 'emulate',
  78.         'new_link'      => false,
  79.         'numrows'       => true,
  80.         'pconnect'      => true,
  81.         'prepare'       => false,
  82.         'ssl'           => false,
  83.         'transactions'  => true,
  84.     );
  85.  
  86.     /**
  87.      * A mapping of native error codes to DB error codes
  88.      * @var array
  89.      */
  90.     // XXX Add here error codes ie: 'S100E' => DB_ERROR_SYNTAX
  91.     var $errorcode_map = array(
  92.         110   => DB_ERROR_VALUE_COUNT_ON_ROW,
  93.         155   => DB_ERROR_NOSUCHFIELD,
  94.         170   => DB_ERROR_SYNTAX,
  95.         207   => DB_ERROR_NOSUCHFIELD,
  96.         208   => DB_ERROR_NOSUCHTABLE,
  97.         245   => DB_ERROR_INVALID_NUMBER,
  98.         515   => DB_ERROR_CONSTRAINT_NOT_NULL,
  99.         547   => DB_ERROR_CONSTRAINT,
  100.         1913  => DB_ERROR_ALREADY_EXISTS,
  101.         2627  => DB_ERROR_CONSTRAINT,
  102.         2714  => DB_ERROR_ALREADY_EXISTS,
  103.         3701  => DB_ERROR_NOSUCHTABLE,
  104.         8134  => DB_ERROR_DIVZERO,
  105.     );
  106.  
  107.     /**
  108.      * The raw database connection created by PHP
  109.      * @var resource
  110.      */
  111.     var $connection;
  112.  
  113.     /**
  114.      * The DSN information for connecting to a database
  115.      * @var array
  116.      */
  117.     var $dsn = array();
  118.  
  119.  
  120.     /**
  121.      * Should data manipulation queries be committed automatically?
  122.      * @var bool
  123.      * @access private
  124.      */
  125.     var $autocommit = true;
  126.  
  127.     /**
  128.      * The quantity of transactions begun
  129.      *
  130.      * {@internal  While this is private, it can't actually be designated
  131.      * private in PHP 5 because it is directly accessed in the test suite.}}
  132.      *
  133.      * @var integer
  134.      * @access private
  135.      */
  136.     var $transaction_opcount = 0;
  137.  
  138.     /**
  139.      * The database specified in the DSN
  140.      *
  141.      * It's a fix to allow calls to different databases in the same script.
  142.      *
  143.      * @var string
  144.      * @access private
  145.      */
  146.     var $_db = null;
  147.  
  148.  
  149.     // }}}
  150.     // {{{ constructor
  151.  
  152.     /**
  153.      * This constructor calls <kbd>$this->DB_common()</kbd>
  154.      *
  155.      * @return void
  156.      */
  157.     function DB_mssql()
  158.     {
  159.         $this->DB_common();
  160.     }
  161.  
  162.     // }}}
  163.     // {{{ connect()
  164.  
  165.     /**
  166.      * Connect to the database server, log in and open the database
  167.      *
  168.      * Don't call this method directly.  Use DB::connect() instead.
  169.      *
  170.      * @param array $dsn         the data source name
  171.      * @param bool  $persistent  should the connection be persistent?
  172.      *
  173.      * @return int  DB_OK on success. A DB_Error object on failure.
  174.      */
  175.     function connect($dsn, $persistent = false)
  176.     {
  177.         if (!PEAR::loadExtension('mssql') && !PEAR::loadExtension('sybase')
  178.             && !PEAR::loadExtension('sybase_ct'))
  179.         {
  180.             return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
  181.         }
  182.  
  183.         $this->dsn = $dsn;
  184.         if ($dsn['dbsyntax']) {
  185.             $this->dbsyntax = $dsn['dbsyntax'];
  186.         }
  187.  
  188.         $params = array(
  189.             $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost',
  190.             $dsn['username'] ? $dsn['username'] : null,
  191.             $dsn['password'] ? $dsn['password'] : null,
  192.         );
  193.         if ($dsn['port']) {
  194.             $params[0] .= ((substr(PHP_OS, 0, 3) == 'WIN') ? ',' : ':')
  195.                         . $dsn['port'];
  196.         }
  197.  
  198.         $connect_function = $persistent ? 'mssql_pconnect' : 'mssql_connect';
  199.  
  200.         $this->connection = @call_user_func_array($connect_function, $params);
  201.  
  202.         if (!$this->connection) {
  203.             return $this->raiseError(DB_ERROR_CONNECT_FAILED,
  204.                                      null, null, null,
  205.                                      @mssql_get_last_message());
  206.         }
  207.         if ($dsn['database']) {
  208.             if (!@mssql_select_db($dsn['database'], $this->connection)) {
  209.                 return $this->raiseError(DB_ERROR_NODBSELECTED,
  210.                                          null, null, null,
  211.                                          @mssql_get_last_message());
  212.             }
  213.             $this->_db = $dsn['database'];
  214.         }
  215.         return DB_OK;
  216.     }
  217.  
  218.     // }}}
  219.     // {{{ disconnect()
  220.  
  221.     /**
  222.      * Disconnects from the database server
  223.      *
  224.      * @return bool  TRUE on success, FALSE on failure
  225.      */
  226.     function disconnect()
  227.     {
  228.         $ret = @mssql_close($this->connection);
  229.         $this->connection = null;
  230.         return $ret;
  231.     }
  232.  
  233.     // }}}
  234.     // {{{ simpleQuery()
  235.  
  236.     /**
  237.      * Sends a query to the database server
  238.      *
  239.      * @param string  the SQL query string
  240.      *
  241.      * @return mixed  + a PHP result resrouce for successful SELECT queries
  242.      *                + the DB_OK constant for other successful queries
  243.      *                + a DB_Error object on failure
  244.      */
  245.     function simpleQuery($query)
  246.     {
  247.         $ismanip = DB::isManip($query);
  248.         $this->last_query = $query;
  249.         if (!@mssql_select_db($this->_db, $this->connection)) {
  250.             return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
  251.         }
  252.         $query = $this->modifyQuery($query);
  253.         if (!$this->autocommit && $ismanip) {
  254.             if ($this->transaction_opcount == 0) {
  255.                 $result = @mssql_query('BEGIN TRAN', $this->connection);
  256.                 if (!$result) {
  257.                     return $this->mssqlRaiseError();
  258.                 }
  259.             }
  260.             $this->transaction_opcount++;
  261.         }
  262.         $result = @mssql_query($query, $this->connection);
  263.         if (!$result) {
  264.             return $this->mssqlRaiseError();
  265.         }
  266.         // Determine which queries that should return data, and which
  267.         // should return an error code only.
  268.         return $ismanip ? DB_OK : $result;
  269.     }
  270.  
  271.     // }}}
  272.     // {{{ nextResult()
  273.  
  274.     /**
  275.      * Move the internal mssql result pointer to the next available result
  276.      *
  277.      * @param a valid fbsql result resource
  278.      *
  279.      * @access public
  280.      *
  281.      * @return true if a result is available otherwise return false
  282.      */
  283.     function nextResult($result)
  284.     {
  285.         return @mssql_next_result($result);
  286.     }
  287.  
  288.     // }}}
  289.     // {{{ fetchInto()
  290.  
  291.     /**
  292.      * Places a row from the result set into the given array
  293.      *
  294.      * Formating of the array and the data therein are configurable.
  295.      * See DB_result::fetchInto() for more information.
  296.      *
  297.      * This method is not meant to be called directly.  Use
  298.      * DB_result::fetchInto() instead.  It can't be declared "protected"
  299.      * because DB_result is a separate object.
  300.      *
  301.      * @param resource $result    the query result resource
  302.      * @param array    $arr       the referenced array to put the data in
  303.      * @param int      $fetchmode how the resulting array should be indexed
  304.      * @param int      $rownum    the row number to fetch (0 = first row)
  305.      *
  306.      * @return mixed  DB_OK on success, NULL when the end of a result set is
  307.      *                 reached or on failure
  308.      *
  309.      * @see DB_result::fetchInto()
  310.      */
  311.     function fetchInto($result, &$arr, $fetchmode, $rownum = null)
  312.     {
  313.         if ($rownum !== null) {
  314.             if (!@mssql_data_seek($result, $rownum)) {
  315.                 return null;
  316.             }
  317.         }
  318.         if ($fetchmode & DB_FETCHMODE_ASSOC) {
  319.             $arr = @mssql_fetch_array($result, MSSQL_ASSOC);
  320.             if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
  321.                 $arr = array_change_key_case($arr, CASE_LOWER);
  322.             }
  323.         } else {
  324.             $arr = @mssql_fetch_row($result);
  325.         }
  326.         if (!$arr) {
  327.             return null;
  328.         }
  329.         if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
  330.             $this->_rtrimArrayValues($arr);
  331.         }
  332.         if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
  333.             $this->_convertNullArrayValuesToEmpty($arr);
  334.         }
  335.         return DB_OK;
  336.     }
  337.  
  338.     // }}}
  339.     // {{{ freeResult()
  340.  
  341.     /**
  342.      * Deletes the result set and frees the memory occupied by the result set
  343.      *
  344.      * This method is not meant to be called directly.  Use
  345.      * DB_result::free() instead.  It can't be declared "protected"
  346.      * because DB_result is a separate object.
  347.      *
  348.      * @param resource $result  PHP's query result resource
  349.      *
  350.      * @return bool  TRUE on success, FALSE if $result is invalid
  351.      *
  352.      * @see DB_result::free()
  353.      */
  354.     function freeResult($result)
  355.     {
  356.         return @mssql_free_result($result);
  357.     }
  358.  
  359.     // }}}
  360.     // {{{ numCols()
  361.  
  362.     /**
  363.      * Gets the number of columns in a result set
  364.      *
  365.      * This method is not meant to be called directly.  Use
  366.      * DB_result::numCols() instead.  It can't be declared "protected"
  367.      * because DB_result is a separate object.
  368.      *
  369.      * @param resource $result  PHP's query result resource
  370.      *
  371.      * @return int  the number of columns.  A DB_Error object on failure.
  372.      *
  373.      * @see DB_result::numCols()
  374.      */
  375.     function numCols($result)
  376.     {
  377.         $cols = @mssql_num_fields($result);
  378.         if (!$cols) {
  379.             return $this->mssqlRaiseError();
  380.         }
  381.         return $cols;
  382.     }
  383.  
  384.     // }}}
  385.     // {{{ numRows()
  386.  
  387.     /**
  388.      * Gets the number of rows in a result set
  389.      *
  390.      * This method is not meant to be called directly.  Use
  391.      * DB_result::numRows() instead.  It can't be declared "protected"
  392.      * because DB_result is a separate object.
  393.      *
  394.      * @param resource $result  PHP's query result resource
  395.      *
  396.      * @return int  the number of rows.  A DB_Error object on failure.
  397.      *
  398.      * @see DB_result::numRows()
  399.      */
  400.     function numRows($result)
  401.     {
  402.         $rows = @mssql_num_rows($result);
  403.         if ($rows === false) {
  404.             return $this->mssqlRaiseError();
  405.         }
  406.         return $rows;
  407.     }
  408.  
  409.     // }}}
  410.     // {{{ autoCommit()
  411.  
  412.     /**
  413.      * Enables or disables automatic commits
  414.      *
  415.      * @param bool $onoff  true turns it on, false turns it off
  416.      *
  417.      * @return int  DB_OK on success.  A DB_Error object if the driver
  418.      *               doesn't support auto-committing transactions.
  419.      */
  420.     function autoCommit($onoff = false)
  421.     {
  422.         // XXX if $this->transaction_opcount > 0, we should probably
  423.         // issue a warning here.
  424.         $this->autocommit = $onoff ? true : false;
  425.         return DB_OK;
  426.     }
  427.  
  428.     // }}}
  429.     // {{{ commit()
  430.  
  431.     /**
  432.      * Commits the current transaction
  433.      *
  434.      * @return int  DB_OK on success.  A DB_Error object on failure.
  435.      */
  436.     function commit()
  437.     {
  438.         if ($this->transaction_opcount > 0) {
  439.             if (!@mssql_select_db($this->_db, $this->connection)) {
  440.                 return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
  441.             }
  442.             $result = @mssql_query('COMMIT TRAN', $this->connection);
  443.             $this->transaction_opcount = 0;
  444.             if (!$result) {
  445.                 return $this->mssqlRaiseError();
  446.             }
  447.         }
  448.         return DB_OK;
  449.     }
  450.  
  451.     // }}}
  452.     // {{{ rollback()
  453.  
  454.     /**
  455.      * Reverts the current transaction
  456.      *
  457.      * @return int  DB_OK on success.  A DB_Error object on failure.
  458.      */
  459.     function rollback()
  460.     {
  461.         if ($this->transaction_opcount > 0) {
  462.             if (!@mssql_select_db($this->_db, $this->connection)) {
  463.                 return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
  464.             }
  465.             $result = @mssql_query('ROLLBACK TRAN', $this->connection);
  466.             $this->transaction_opcount = 0;
  467.             if (!$result) {
  468.                 return $this->mssqlRaiseError();
  469.             }
  470.         }
  471.         return DB_OK;
  472.     }
  473.  
  474.     // }}}
  475.     // {{{ affectedRows()
  476.  
  477.     /**
  478.      * Determines the number of rows affected by a data maniuplation query
  479.      *
  480.      * 0 is returned for queries that don't manipulate data.
  481.      *
  482.      * @return int  the number of rows.  A DB_Error object on failure.
  483.      */
  484.     function affectedRows()
  485.     {
  486.         if (DB::isManip($this->last_query)) {
  487.             $res = @mssql_query('select @@rowcount', $this->connection);
  488.             if (!$res) {
  489.                 return $this->mssqlRaiseError();
  490.             }
  491.             $ar = @mssql_fetch_row($res);
  492.             if (!$ar) {
  493.                 $result = 0;
  494.             } else {
  495.                 @mssql_free_result($res);
  496.                 $result = $ar[0];
  497.             }
  498.         } else {
  499.             $result = 0;
  500.         }
  501.         return $result;
  502.     }
  503.  
  504.     // }}}
  505.     // {{{ nextId()
  506.  
  507.     /**
  508.      * Returns the next free id in a sequence
  509.      *
  510.      * @param string  $seq_name  name of the sequence
  511.      * @param boolean $ondemand  when true, the seqence is automatically
  512.      *                            created if it does not exist
  513.      *
  514.      * @return int  the next id number in the sequence.
  515.      *               A DB_Error object on failure.
  516.      *
  517.      * @see DB_common::nextID(), DB_common::getSequenceName(),
  518.      *      DB_mssql::createSequence(), DB_mssql::dropSequence()
  519.      */
  520.     function nextId($seq_name, $ondemand = true)
  521.     {
  522.         $seqname = $this->getSequenceName($seq_name);
  523.         if (!@mssql_select_db($this->_db, $this->connection)) {
  524.             return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
  525.         }
  526.         $repeat = 0;
  527.         do {
  528.             $this->pushErrorHandling(PEAR_ERROR_RETURN);
  529.             $result = $this->query("INSERT INTO $seqname (vapor) VALUES (0)");
  530.             $this->popErrorHandling();
  531.             if ($ondemand && DB::isError($result) &&
  532.                 ($result->getCode() == DB_ERROR || $result->getCode() == DB_ERROR_NOSUCHTABLE))
  533.             {
  534.                 $repeat = 1;
  535.                 $result = $this->createSequence($seq_name);
  536.                 if (DB::isError($result)) {
  537.                     return $this->raiseError($result);
  538.                 }
  539.             } elseif (!DB::isError($result)) {
  540.                 $result =& $this->query("SELECT @@IDENTITY FROM $seqname");
  541.                 $repeat = 0;
  542.             } else {
  543.                 $repeat = false;
  544.             }
  545.         } while ($repeat);
  546.         if (DB::isError($result)) {
  547.             return $this->raiseError($result);
  548.         }
  549.         $result = $result->fetchRow(DB_FETCHMODE_ORDERED);
  550.         return $result[0];
  551.     }
  552.  
  553.     /**
  554.      * Creates a new sequence
  555.      *
  556.      * @param string $seq_name  name of the new sequence
  557.      *
  558.      * @return int  DB_OK on success.  A DB_Error object on failure.
  559.      *
  560.      * @see DB_common::createSequence(), DB_common::getSequenceName(),
  561.      *      DB_mssql::nextID(), DB_mssql::dropSequence()
  562.      */
  563.     function createSequence($seq_name)
  564.     {
  565.         return $this->query('CREATE TABLE '
  566.                             . $this->getSequenceName($seq_name)
  567.                             . ' ([id] [int] IDENTITY (1, 1) NOT NULL,'
  568.                             . ' [vapor] [int] NULL)');
  569.     }
  570.  
  571.     // }}}
  572.     // {{{ dropSequence()
  573.  
  574.     /**
  575.      * Deletes a sequence
  576.      *
  577.      * @param string $seq_name  name of the sequence to be deleted
  578.      *
  579.      * @return int  DB_OK on success.  A DB_Error object on failure.
  580.      *
  581.      * @see DB_common::dropSequence(), DB_common::getSequenceName(),
  582.      *      DB_mssql::nextID(), DB_mssql::createSequence()
  583.      */
  584.     function dropSequence($seq_name)
  585.     {
  586.         return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
  587.     }
  588.  
  589.     // }}}
  590.     // {{{ quoteIdentifier()
  591.  
  592.     /**
  593.      * Quotes a string so it can be safely used as a table or column name
  594.      *
  595.      * @param string $str  identifier name to be quoted
  596.      *
  597.      * @return string  quoted identifier string
  598.      *
  599.      * @see DB_common::quoteIdentifier()
  600.      * @since Method available since Release 1.6.0
  601.      */
  602.     function quoteIdentifier($str)
  603.     {
  604.         return '[' . str_replace(']', ']]', $str) . ']';
  605.     }
  606.  
  607.     // }}}
  608.     // {{{ mssqlRaiseError()
  609.  
  610.     /**
  611.      * Produces a DB_Error object regarding the current problem
  612.      *
  613.      * @param int $errno  if the error is being manually raised pass a
  614.      *                     DB_ERROR* constant here.  If this isn't passed
  615.      *                     the error information gathered from the DBMS.
  616.      *
  617.      * @return object  the DB_Error object
  618.      *
  619.      * @see DB_common::raiseError(),
  620.      *      DB_mssql::errorNative(), DB_mssql::errorCode()
  621.      */
  622.     function mssqlRaiseError($code = null)
  623.     {
  624.         $message = @mssql_get_last_message();
  625.         if (!$code) {
  626.             $code = $this->errorNative();
  627.         }
  628.         return $this->raiseError($this->errorCode($code, $message),
  629.                                  null, null, null, "$code - $message");
  630.     }
  631.  
  632.     // }}}
  633.     // {{{ errorNative()
  634.  
  635.     /**
  636.      * Gets the DBMS' native error code produced by the last query
  637.      *
  638.      * @return int  the DBMS' error code
  639.      */
  640.     function errorNative()
  641.     {
  642.         $res = @mssql_query('select @@ERROR as ErrorCode', $this->connection);
  643.         if (!$res) {
  644.             return DB_ERROR;
  645.         }
  646.         $row = @mssql_fetch_row($res);
  647.         return $row[0];
  648.     }
  649.  
  650.     // }}}
  651.     // {{{ errorCode()
  652.  
  653.     /**
  654.      * Determines PEAR::DB error code from mssql's native codes.
  655.      *
  656.      * If <var>$nativecode</var> isn't known yet, it will be looked up.
  657.      *
  658.      * @param  mixed  $nativecode  mssql error code, if known
  659.      * @return integer  an error number from a DB error constant
  660.      * @see errorNative()
  661.      */
  662.     function errorCode($nativecode = null, $msg = '')
  663.     {
  664.         if (!$nativecode) {
  665.             $nativecode = $this->errorNative();
  666.         }
  667.         if (isset($this->errorcode_map[$nativecode])) {
  668.             if ($nativecode == 3701
  669.                 && preg_match('/Cannot drop the index/i', $msg))
  670.             {
  671.                 return DB_ERROR_NOT_FOUND;
  672.             }
  673.             return $this->errorcode_map[$nativecode];
  674.         } else {
  675.             return DB_ERROR;
  676.         }
  677.     }
  678.  
  679.     // }}}
  680.     // {{{ tableInfo()
  681.  
  682.     /**
  683.      * Returns information about a table or a result set
  684.      *
  685.      * NOTE: only supports 'table' and 'flags' if <var>$result</var>
  686.      * is a table name.
  687.      *
  688.      * @param object|string  $result  DB_result object from a query or a
  689.      *                                 string containing the name of a table.
  690.      *                                 While this also accepts a query result
  691.      *                                 resource identifier, this behavior is
  692.      *                                 deprecated.
  693.      * @param int            $mode    a valid tableInfo mode
  694.      *
  695.      * @return array  an associative array with the information requested.
  696.      *                 A DB_Error object on failure.
  697.      *
  698.      * @see DB_common::tableInfo()
  699.      */
  700.     function tableInfo($result, $mode = null)
  701.     {
  702.         if (is_string($result)) {
  703.             /*
  704.              * Probably received a table name.
  705.              * Create a result resource identifier.
  706.              */
  707.             if (!@mssql_select_db($this->_db, $this->connection)) {
  708.                 return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
  709.             }
  710.             $id = @mssql_query("SELECT * FROM $result WHERE 1=0",
  711.                                $this->connection);
  712.             $got_string = true;
  713.         } elseif (isset($result->result)) {
  714.             /*
  715.              * Probably received a result object.
  716.              * Extract the result resource identifier.
  717.              */
  718.             $id = $result->result;
  719.             $got_string = false;
  720.         } else {
  721.             /*
  722.              * Probably received a result resource identifier.
  723.              * Copy it.
  724.              * Deprecated.  Here for compatibility only.
  725.              */
  726.             $id = $result;
  727.             $got_string = false;
  728.         }
  729.  
  730.         if (!is_resource($id)) {
  731.             return $this->mssqlRaiseError(DB_ERROR_NEED_MORE_DATA);
  732.         }
  733.  
  734.         if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
  735.             $case_func = 'strtolower';
  736.         } else {
  737.             $case_func = 'strval';
  738.         }
  739.  
  740.         $count = @mssql_num_fields($id);
  741.         $res   = array();
  742.  
  743.         if ($mode) {
  744.             $res['num_fields'] = $count;
  745.         }
  746.  
  747.         for ($i = 0; $i < $count; $i++) {
  748.             $res[$i] = array(
  749.                 'table' => $got_string ? $case_func($result) : '',
  750.                 'name'  => $case_func(@mssql_field_name($id, $i)),
  751.                 'type'  => @mssql_field_type($id, $i),
  752.                 'len'   => @mssql_field_length($id, $i),
  753.                 // We only support flags for table
  754.                 'flags' => $got_string
  755.                            ? $this->_mssql_field_flags($result,
  756.                                                        @mssql_field_name($id, $i))
  757.                            : '',
  758.             );
  759.             if ($mode & DB_TABLEINFO_ORDER) {
  760.                 $res['order'][$res[$i]['name']] = $i;
  761.             }
  762.             if ($mode & DB_TABLEINFO_ORDERTABLE) {
  763.                 $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
  764.             }
  765.         }
  766.  
  767.         // free the result only if we were called on a table
  768.         if ($got_string) {
  769.             @mssql_free_result($id);
  770.         }
  771.         return $res;
  772.     }
  773.  
  774.     // }}}
  775.     // {{{ _mssql_field_flags()
  776.  
  777.     /**
  778.      * Get a column's flags
  779.      *
  780.      * Supports "not_null", "primary_key",
  781.      * "auto_increment" (mssql identity), "timestamp" (mssql timestamp),
  782.      * "unique_key" (mssql unique index, unique check or primary_key) and
  783.      * "multiple_key" (multikey index)
  784.      *
  785.      * mssql timestamp is NOT similar to the mysql timestamp so this is maybe
  786.      * not useful at all - is the behaviour of mysql_field_flags that primary
  787.      * keys are alway unique? is the interpretation of multiple_key correct?
  788.      *
  789.      * @param string $table   the table name
  790.      * @param string $column  the field name
  791.      *
  792.      * @return string  the flags
  793.      *
  794.      * @access private
  795.      * @author Joern Barthel <j_barthel@web.de>
  796.      */
  797.     function _mssql_field_flags($table, $column)
  798.     {
  799.         static $tableName = null;
  800.         static $flags = array();
  801.  
  802.         if ($table != $tableName) {
  803.  
  804.             $flags = array();
  805.             $tableName = $table;
  806.  
  807.             // get unique and primary keys
  808.             $res = $this->getAll("EXEC SP_HELPINDEX[$table]", DB_FETCHMODE_ASSOC);
  809.  
  810.             foreach ($res as $val) {
  811.                 $keys = explode(', ', $val['index_keys']);
  812.  
  813.                 if (sizeof($keys) > 1) {
  814.                     foreach ($keys as $key) {
  815.                         $this->_add_flag($flags[$key], 'multiple_key');
  816.                     }
  817.                 }
  818.  
  819.                 if (strpos($val['index_description'], 'primary key')) {
  820.                     foreach ($keys as $key) {
  821.                         $this->_add_flag($flags[$key], 'primary_key');
  822.                     }
  823.                 } elseif (strpos($val['index_description'], 'unique')) {
  824.                     foreach ($keys as $key) {
  825.                         $this->_add_flag($flags[$key], 'unique_key');
  826.                     }
  827.                 }
  828.             }
  829.  
  830.             // get auto_increment, not_null and timestamp
  831.             $res = $this->getAll("EXEC SP_COLUMNS[$table]", DB_FETCHMODE_ASSOC);
  832.  
  833.             foreach ($res as $val) {
  834.                 $val = array_change_key_case($val, CASE_LOWER);
  835.                 if ($val['nullable'] == '0') {
  836.                     $this->_add_flag($flags[$val['column_name']], 'not_null');
  837.                 }
  838.                 if (strpos($val['type_name'], 'identity')) {
  839.                     $this->_add_flag($flags[$val['column_name']], 'auto_increment');
  840.                 }
  841.                 if (strpos($val['type_name'], 'timestamp')) {
  842.                     $this->_add_flag($flags[$val['column_name']], 'timestamp');
  843.                 }
  844.             }
  845.         }
  846.  
  847.         if (array_key_exists($column, $flags)) {
  848.             return(implode(' ', $flags[$column]));
  849.         }
  850.         return '';
  851.     }
  852.  
  853.     // }}}
  854.     // {{{ _add_flag()
  855.  
  856.     /**
  857.      * Adds a string to the flags array if the flag is not yet in there
  858.      * - if there is no flag present the array is created
  859.      *
  860.      * @param array  &$array  the reference to the flag-array
  861.      * @param string $value   the flag value
  862.      *
  863.      * @return void
  864.      *
  865.      * @access private
  866.      * @author Joern Barthel <j_barthel@web.de>
  867.      */
  868.     function _add_flag(&$array, $value)
  869.     {
  870.         if (!is_array($array)) {
  871.             $array = array($value);
  872.         } elseif (!in_array($value, $array)) {
  873.             array_push($array, $value);
  874.         }
  875.     }
  876.  
  877.     // }}}
  878.     // {{{ getSpecialQuery()
  879.  
  880.     /**
  881.      * Obtains the query string needed for listing a given type of objects
  882.      *
  883.      * @param string $type  the kind of objects you want to retrieve
  884.      *
  885.      * @return string  the SQL query string or null if the driver doesn't
  886.      *                  support the object type requested
  887.      *
  888.      * @access protected
  889.      * @see DB_common::getListOf()
  890.      */
  891.     function getSpecialQuery($type)
  892.     {
  893.         switch ($type) {
  894.             case 'tables':
  895.                 return "SELECT name FROM sysobjects WHERE type = 'U'"
  896.                        . ' ORDER BY name';
  897.             case 'views':
  898.                 return "SELECT name FROM sysobjects WHERE type = 'V'";
  899.             default:
  900.                 return null;
  901.         }
  902.     }
  903.  
  904.     // }}}
  905. }
  906.  
  907. /*
  908.  * Local variables:
  909.  * tab-width: 4
  910.  * c-basic-offset: 4
  911.  * End:
  912.  */
  913.  
  914. ?>
  915.